package com.example.apicommon;

import javafx.beans.value.ChangeListener;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 方法引用通过方法的名字来指向一个方法。

 方法引用可以使语言的构造更紧凑简洁，减少冗余代码。

 方法引用使用一对冒号 :: 。

 下面，我们在 Car 类中定义了 4 个方法作为例子来区分 Java 中 4 种不同方法的引用。
 **/
public class MethodReference {
    public static class TestUtil {
        public static boolean isBiggerThan3(int input) {return input > 3;}
        public void printDetail(Student student) {System.out.println(student.toString());}
    }
    public static final void staticMethodReference() {
        /**
         * static method 的 方法引用
         * 语法格式: className::staticMethod
         * p1 匿名内部类形式
         * p2 lambda表达式   (args) -> Class.staticMethod(args)
         * p3 方法引用       Class::staticMethod
         * 由此可见：方法引用的实质是 只有一行代码的lambda表达式的替代
         */
        Predicate<Integer> p1 = new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return TestUtil.isBiggerThan3(integer);
            }
        };
        Predicate<Integer> p2 = a -> TestUtil.isBiggerThan3(a);
        Predicate<Integer> p3 = TestUtil::isBiggerThan3;

        Stream.of(1, 2, 3, 4, 5).filter(p3).forEach(System.out::println);
    }

    public static class Student {
        private String name; private int age;
        public Student(String name, int age) {this.name = name;this.age = age;}
        public String getStatus(String thing) {return String.format("%d岁的%s正在%s", age, name, thing);}
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
        public static String getStudentStatus(Student student, String action, BiFunction<Student, String, String> biFunction) {
            return biFunction.apply(student, action);
        }
        public static void consumeStudent(Student student, Consumer<Student> consumer) {
            consumer.accept(student);
        }

        public static Student getStudent(String name, int age, BiFunction<String, Integer, Student> biFunction) {
            return biFunction.apply(name, age);
        }
    }
    public static final void paramIsInstanceMethodReference() {
        /** 入参是实例的方法 的方法引用
         * (obj, args) -> obj.instanceMethod(args)
         * ObjectType::instanceMethod
         */
        // BiFunction<methodParam1, methodParam2, methodReturn>
        BiFunction<Student, String, String> b1 = new BiFunction<Student, String, String>() {
            @Override
            public String apply(Student stu, String status) {
                return stu.getStatus(status);
            }
        };
        BiFunction<Student, String, String> b2 = (Student stu, String status) -> stu.getStatus(status); // stu is obj
        BiFunction<Student, String, String> b3 = Student::getStatus; // Student is ObjectType

        System.out.println(Student.getStudentStatus(new Student("cyl", 21), "sleep", b3));
    }

    public static final void instanceMethodReference() {
        /** 调用已经存在的实例的方法 的 方法引用
         * (args) -> obj.instanceMethod(args)
         * obj::instanceMethod
         */
        TestUtil util = new TestUtil();
        Consumer<Student> c1 = new Consumer<Student>() {
            @Override
            public void accept(Student student) {
                util.printDetail(student);
                // util = null; // Variable 'util' is accessed from within inner class, needs to be final or effectively final
            }
        };
        // 可见 utilObj 对象是我们提前new出来的，是已经存在了的对象，不是Lambda c2 的入参
        Consumer<Student> c2 = (Student student) -> util.printDetail(student); // obj.instanceMethod()
        Consumer<Student> c3 = util::printDetail; // obj::instanceMethod

        Student.consumeStudent(new Student("cyl", 21), c3);
    }

    public static final void newMethodReference() {
        /** 调用类的构造函数
         * (args) -> new ClassName(args)
         * ClassName::new
         */
        BiFunction<String, Integer, Student> b1 = new BiFunction<String, Integer, Student>() {
            @Override
            public Student apply(String name, Integer age) {
                return new Student(name, age);
            }
        };
        // BiFunction<methodParam1, methodParam2, methodReturn>
        BiFunction<String, Integer, Student> b2 = (name, age) -> new Student(name, age);
        BiFunction<String, Integer, Student> b3 = Student::new;

        Student cyl = Student.getStudent("cyl", 21, b3); // b1 b2 b3 实际就是一个 new Student
    }

    public static void main(String[] args) {
        Consumer<String> c1 = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        Consumer<String> c2 = s -> System.out.println(s);
        Consumer<String> c3 = System.out::println;

        c1.accept("454545");
        c2.accept("454545");
        c3.accept("454545");
    }

}
